﻿using System.ServiceModel;
using System.Collections.Concurrent;
using System.ServiceModel.Channels;
using System.Net;
using System;

namespace Lypshare.Net.Server.Service
{
	/// <summary>
	/// Access service.
	/// </summary>
	/// <exception cref='ArgumentNullException'>
	/// Is thrown when the argument null exception.
	/// </exception>
    [ServiceBehavior(InstanceContextMode = InstanceContextMode.Single,
        ConcurrencyMode = ConcurrencyMode.Multiple, UseSynchronizationContext = false)]
    public class AccessService : IAccessService
    {
        // --------------------------------------------------------
        // Class Variables
        // --------------------------------------------------------


        /// <summary>
        /// The _scopes.
        /// </summary>
        private ConcurrentDictionary<int, Scope> _scopes 
            = new ConcurrentDictionary<int, Scope>();


        // --------------------------------------------------------
        // Class Properties
        // --------------------------------------------------------


        /// <summary>
        /// Gets the current end point.
        /// </summary>
        /// <value>
        /// The current end point.
        /// </value>
        public IPEndPoint CurrentEndPoint
        {           get
            {
                var properties = OperationContext
                   .Current.IncomingMessageProperties;

                var property = properties[
                    RemoteEndpointMessageProperty.Name]
                    as RemoteEndpointMessageProperty;

                var endPoint = new IPEndPoint(IPAddress.Parse(
                    property.Address), property.Port);

                return endPoint;

            } // get

        } // Property: CurrentEndPoint


        /// <summary>
        /// Gets the current identifier.
        /// </summary>
        /// <value>
        /// The current identifier.
        /// </value>
        public int CurrentId
        {
            get { return CurrentEndPoint
                .GetHashCode(); }

        } // Property: CurrentId


        /// <summary>
        /// Gets the current client.
        /// </summary>
        /// <value>
        /// The current client.
        /// </value>
        public Client CurrentClient
        {
            get { return ClientManager
                .Get(CurrentId); }

        } // Property: CurrentSession



        // --------------------------------------------------------
        // Class Functions
        // --------------------------------------------------------


        /// <summary>
        /// Gets the scope.
        /// </summary>
        /// <returns>
        /// The scope.
        /// </returns>
        /// <param name='scope'>
        /// Scope.
        /// </param>
        /// <exception cref='ArgumentNullException'>
        /// Is thrown when an argument passed to a method is invalid because it is <see langword="null" /> .
        /// </exception>
        private Scope GetScope(String scope)
        {
            if(scope == null)
                throw new ArgumentNullException("scope");

            // Load the scope id
            var id = scope.GetHashCode();

            return _scopes.GetOrAdd(id, (key) => 
                new Scope(scope));

        } // GetScope


        /// <summary>
        /// Register the specified address and authed.
        /// </summary>
        /// <param name='address'>
        /// Address.
        /// </param>
        /// <param name='authed'>
        /// Authed.
        /// </param>
        public int Register(out IPAddress address, out bool authed)
        {
            if (!ClientManager.Contains(CurrentId))
            {
                // Load current callback function
                var callback = OperationContext.Current
                    .GetCallbackChannel<IAccessServiceCallback>();
				
				var client = new Client(CurrentId, 
                    CurrentEndPoint.Address, callback);
				
                ClientManager.Add(client);

            } // if (!ClientManager.Contains(CurrentId))

			address = CurrentClient.Address;
            authed = CurrentClient.IsAuthed;

            return CurrentId;

        } // Register


        /// <summary>
        /// Auth the specified username and password.
        /// </summary>
        /// <param name='username'>
        /// If set to <c>true</c> username.
        /// </param>
        /// <param name='password'>
        /// If set to <c>true</c> password.
        /// </param>
        /// <exception cref='ArgumentNullException'>
        /// Is thrown when an argument passed to a method is invalid because it is <see langword="null" /> .
        /// </exception>
        public bool Auth(String username, String password)
        {
            if (username == null) throw new ArgumentNullException("username");
            if (password == null) throw new ArgumentNullException("password");

            return CurrentClient.Auth(
                username, password);

        } // Auth


        /// <summary>
        /// Join the specified scope.
        /// </summary>
        /// <param name='scope'>
        /// Scope.
        /// </param>
        /// <exception cref='ArgumentNullException'>
        /// Is thrown when an argument passed to a method is invalid because it is <see langword="null" /> .
        /// </exception>
        public void Join(String scope)
        {
            if (scope == null)
                throw new ArgumentNullException("scope");

            CurrentClient.Join(
                GetScope(scope));

        } // Join

        
        /// <summary>
        /// Syncronize the specified clientId and port.
        /// </summary>
        /// <param name='clientId'>
        /// Client identifier.
        /// </param>
        /// <param name='port'>
        /// Port.
        /// </param>
        public int Syncronize(int clientId, int port)
		{
			return CurrentClient.Synchronize(
				ClientManager.Get(clientId), port);
			
		} // Synchronize


        /// <summary>
        /// Keeps the connection.
        /// </summary>
        public void KeepConnection()
        {
            CurrentClient
                .KeepConnection();

        } // KeepConnection


        /// <summary>
        /// Disconnect this instance.
        /// </summary>
        public void Disconnect()
        {
            CurrentClient
                .Disconnect();

        } // Disconnect

    } // class AccessService
} // namespace Lypshare.Net.Server.Service